function success = importCSVasReq(parentFigure, messageTextArea, themeName)
%IMPORTCSVASTOREQ 完整的CSV导入到Requirements Toolbox功能
%   支持用户选择CSV文件、配置列映射、选择目标slreqx文件
%   包含数据验证、进度显示和错误处理
%
%输入参数:
%   parentFigure   - 父窗口句柄，用于模态对话框和进度条
%   messageTextArea - 消息文本区域句柄，用于显示状态信息
%   themeName      - (可选) 主题名称，用于对话框样式 (如 'light', 'dark', 'tech')
%
%输出参数:
%   success - 导入成功返回true，失败或取消返回false

% 处理可选的主题参数
if nargin < 3
    themeName = 'light'; % 默认浅色主题
end
%
%功能特性:
%   - 智能列映射配置，支持中英文列名自动识别
%   - 支持新建或选择现有slreqx文件
%   - 完整的数据验证（重复检查、格式检查）
%   - 实时进度显示和详细错误日志
%   - 支持增量更新现有需求
%
%支持的需求属性:
%   - 需求ID、需求内容、需求摘要、需求描述
%   - 状态、版本、作者、优先级
%   - 自定义属性1-5
%
%使用示例:
%   success = importCSVasReq(app.MainFigure, app.MessagesTextArea);
%
%依赖项:
%   - MATLAB Requirements Toolbox
%   - MATLAB R2020b及以上版本（支持uigridlayout）
%
%作者: MBD Platform Team
%版本: 1.0
%日期: 2024

success = false;

try
    % 0. 加载配置
    attributeConfig = loadAttributeConfig();
    updateMessage(messageTextArea, '加载配置成功');
    
    % 1. 选择CSV文件
    [csvFileName, csvPath] = uigetfile('*.csv', '选择CSV需求文件');
    if isequal(csvFileName, 0)
        return; % 用户取消
    end
    csvFullPath = fullfile(csvPath, csvFileName);
    
    updateMessage(messageTextArea, sprintf('选择CSV文件: %s', csvFileName));
    
    % 2. 读取并预览CSV文件
    csvData = readCSVFile(csvFullPath, parentFigure, messageTextArea);
    if isempty(csvData)
        uialert(parentFigure, 'CSV文件读取失败或文件为空！', '错误', 'Icon', 'error');
        return;
    end
    
    % 3. 配置列映射
    columnMapping = configureColumnMapping(csvData, parentFigure, attributeConfig, themeName);
    if isempty(columnMapping)
        return; % 用户取消
    end
    
    % 4. 选择目标slreqx文件
    [targetReqSet, isNewFile] = selectTargetRequirementSet(parentFigure, messageTextArea);
    if isempty(targetReqSet)
        return; % 用户取消
    end
    
    % 5. 数据验证
    updateMessage(messageTextArea, '正在验证数据格式...');
    
    [isValid, validationErrors] = validateCSVData(csvData, columnMapping, attributeConfig);
    if ~isValid
        showValidationErrors(validationErrors, parentFigure);
        return;
    end
    
    % 6. 在需求集上注册自定义属性
    updateMessage(messageTextArea, '正在注册自定义属性...');
    registerCustomAttributes(targetReqSet, columnMapping, attributeConfig);
    
    % 7. 执行导入
    updateMessage(messageTextArea, '开始导入需求数据...');
    
    success = executeRequirementImport(csvData, columnMapping, targetReqSet, isNewFile, parentFigure, messageTextArea, attributeConfig);
    
catch ME
    uialert(parentFigure, sprintf('CSV导入过程发生错误：\n%s', ME.message), '错误', 'Icon', 'error');
    success = false;
end

end

% ===== 辅助函数 =====

function attributeConfig = loadAttributeConfig()
%LOADATTRIBUTECONFIG 从YML文件加载需求属性配置
%   返回包含属性定义的结构体，加载失败时直接抛出错误
%   基于第7条规则：使用yamltool进行YAML文件读取

% 获取YML配置文件路径
configPath = fullfile(fileparts(mfilename('fullpath')), 'requirement_management_config.yml');

% 检查文件是否存在
if ~exist(configPath, 'file')
    error('IMPORT_CSV:ConfigFileNotFound', '属性配置文件未找到: %s\n请确保文件存在并且路径正确。', configPath);
end

try
    % 使用yamlread读取YML文件
    attributeConfig = yamlread(configPath);
    
catch ME
    error('IMPORT_CSV:ConfigFileReadFailed', '读取属性配置文件失败: %s\n错误信息: %s', configPath, ME.message);
end

% 验证配置结构
if ~isfield(attributeConfig, 'attributes') || ~isfield(attributeConfig, 'validationRules')
    error('IMPORT_CSV:ConfigFileInvalidStructure', '属性配置文件结构不完整: %s\n必须包含 "attributes" 和 "validationRules" 字段。', configPath);
end

% 进一步验证attributes结构
if ~isstruct(attributeConfig.attributes) || isempty(fieldnames(attributeConfig.attributes))
    error('IMPORT_CSV:ConfigFileInvalidAttributes', '属性配置文件中的 "attributes" 字段无效: %s\n必须是非空的结构体。', configPath);
end

% 验证validationRules结构
if ~isstruct(attributeConfig.validationRules)
    error('IMPORT_CSV:ConfigFileInvalidValidationRules', '属性配置文件中的 "validationRules" 字段无效: %s\n必须是结构体。', configPath);
end

fprintf('成功加载属性配置文件: %s\n', configPath);
end


function updateMessage(messageTextArea, msg)
%UPDATEMESSAGE 更新消息文本区域
if ~isempty(messageTextArea) && isvalid(messageTextArea)
    messageTextArea.Value = msg;
    drawnow;
end
end

function csvData = readCSVFile(csvFilePath, parentFigure, messageTextArea)
%READCSVFILE 读取CSV文件并返回数据
csvData = [];

try
    % 使用readtable读取CSV，自动处理编码和分隔符
    opts = detectImportOptions(csvFilePath);
    opts.VariableNamingRule = 'preserve'; % 保留原始列名
    
    csvData = readtable(csvFilePath, opts);
    
    if height(csvData) == 0
        uialert(parentFigure, 'CSV文件为空！', '警告', 'Icon', 'warning');
        return;
    end
    
    updateMessage(messageTextArea, sprintf('成功读取CSV文件，共%d行数据', height(csvData)));
    
catch ME
    uialert(parentFigure, sprintf('读取CSV文件失败：\n%s', ME.message), '错误', 'Icon', 'error');
end
end

function columnMapping = configureColumnMapping(csvData, parentFigure, attributeConfig, themeName)
%CONFIGURECOLUMNMAPPING 配置CSV列到需求属性的映射
% 添加themeName参数以支持主题应用
columnMapping = [];

try
    % 获取CSV列名
    columnNames = csvData.Properties.VariableNames;
    
    % 获取映射设置
    mappingSettings = struct('unmatchedColumnsAsCustom', true, ...
                            'showUnmatchedColumnsDialog', true, ...
                            'preserveOriginalColumnNames', true);
    if isfield(attributeConfig, 'mappingSettings')
        mappingSettings = attributeConfig.mappingSettings;
    end
    
    % 创建列映射配置对话框（带自动映射功能）
    columnMapping = createColumnMappingDialog(columnNames, csvData, parentFigure, attributeConfig, themeName);
    
    if isempty(columnMapping)
        return; % 用户取消
    end
    
    % 处理未匹配的列
    if mappingSettings.unmatchedColumnsAsCustom && mappingSettings.showUnmatchedColumnsDialog
        columnMapping = handleUnmatchedColumns(columnMapping, columnNames, csvData, parentFigure, mappingSettings, themeName);
    end
    
catch ME
    uialert(parentFigure, sprintf('配置列映射失败：\n%s', ME.message), '错误', 'Icon', 'error');
end
end

function [targetReqSet, isNewFile] = selectTargetRequirementSet(parentFigure, messageTextArea)
%SELECTTARGETREQUIREMENTSET 选择目标需求集文件
targetReqSet = [];
isNewFile = false;

% 创建选择对话框
choice = questdlg('请选择目标需求集文件', '需求集选择', ...
                 '新建需求集文件', '选择现有文件', '取消', '新建需求集文件');

switch choice
    case '新建需求集文件'
        % 基于规则第3条：使用ProjectPathManager获取默认路径
        try
            defaultPath = ProjectPathManager.getDirPath('requirements specification');
        catch
            % 如果获取失败，使用当前目录
            defaultPath = pwd;
        end
        
        [fileName, pathName] = uiputfile('*.slreqx', '创建新的需求集文件', fullfile(defaultPath, 'new_requirements.slreqx'));
        if isequal(fileName, 0)
            return;
        end
        
        filePath = fullfile(pathName, fileName);
        
        % 创建新的需求集
        try
            targetReqSet = slreq.new(filePath);
            isNewFile = true;
            updateMessage(messageTextArea, sprintf('创建新需求集: %s', fileName));
        catch ME
            uialert(parentFigure, sprintf('创建需求集失败：\n%s', ME.message), '错误', 'Icon', 'error');
            return;
        end
        
    case '选择现有文件'
        % 基于规则第3条：使用ProjectPathManager获取默认路径
        try
            defaultPath = ProjectPathManager.getDirPath('requirements specification');
        catch
            % 如果获取失败，使用当前目录
            defaultPath = pwd;
        end
        
        [fileName, pathName] = uigetfile('*.slreqx', '选择现有需求集文件', defaultPath);
        if isequal(fileName, 0)
            return;
        end
        
        filePath = fullfile(pathName, fileName);
        
        % 打开现有需求集
        try
            targetReqSet = slreq.open(filePath);
            isNewFile = false;
            updateMessage(messageTextArea, sprintf('打开现有需求集: %s', fileName));
        catch ME
            uialert(parentFigure, sprintf('打开需求集失败：\n%s', ME.message), '错误', 'Icon', 'error');
            return;
        end
        
    case '取消'
        return;
end

drawnow;
end

function [isValid, errors] = validateCSVData(csvData, columnMapping, attributeConfig)
%VALIDATECSVDATA 验证CSV数据的有效性
isValid = true;
errors = {};

try
    % 使用传入的属性配置
    validationRules = attributeConfig.validationRules;
    
    mappedColumns = fieldnames(columnMapping);
    selectedAttributes = {};
    
    % 收集所有映射的属性
    for i = 1:length(mappedColumns)
        mappingInfo = columnMapping.(mappedColumns{i});
        selectedAttributes{end+1} = mappingInfo.attribute;
    end
    
    % 检查唯一性字段
    if isfield(validationRules, 'uniqueFields')
        for uniqueField = validationRules.uniqueFields
            fieldKey = uniqueField{1}; % 英文键名
            
            % 根据英文键名查找对应的displayName
            displayName = '';
            attributeNames = fieldnames(attributeConfig.attributes);
            for j = 1:length(attributeNames)
                if strcmp(attributeNames{j}, fieldKey)
                    displayName = attributeConfig.attributes.(fieldKey).displayName;
                    break;
                end
            end
            
            if ~isempty(displayName) && ismember(displayName, selectedAttributes)
                % 找到对应的列
                for i = 1:length(mappedColumns)
                    mappingInfo = columnMapping.(mappedColumns{i});
                    if strcmp(mappingInfo.attribute, displayName)
                        originalColumnName = mappingInfo.originalColumn;
                        
                        % 验证唯一性
                        values = csvData.(originalColumnName);
                        if length(unique(values)) ~= length(values)
                            errors{end+1} = sprintf('%s列"%s"包含重复值', displayName, originalColumnName);
                            isValid = false;
                        end
                        
                        % 验证非空
                        if any(ismissing(values) | cellfun(@isempty, string(values)))
                            errors{end+1} = sprintf('%s列"%s"包含空值', displayName, originalColumnName);
                            isValid = false;
                        end
                        break;
                    end
                end
            end
        end
    end
    
    % 检查必需字段的非空性
    for i = 1:length(mappedColumns)
        mappingInfo = columnMapping.(mappedColumns{i});
        originalColumnName = mappingInfo.originalColumn;
        attribute = mappingInfo.attribute;
        
        % 查找属性配置
        attributeNames = fieldnames(attributeConfig.attributes);
        for j = 1:length(attributeNames)
            attrName = attributeNames{j};
            attrConfig = attributeConfig.attributes.(attrName);
            if strcmp(attrConfig.displayName, attribute)
                % 如果是必需字段，验证非空性
                if isfield(attrConfig, 'required') && attrConfig.required
                    content = csvData.(originalColumnName);
                    if any(ismissing(content) | cellfun(@isempty, string(content)))
                        errors{end+1} = sprintf('%s列"%s"包含空值', attribute, originalColumnName);
                        isValid = false;
                    end
                end
                
                % 日期类型验证
                if isfield(attrConfig, 'type') && strcmp(attrConfig.type, 'datetime')
                    try
                        dateValues = csvData.(originalColumnName);
                        % 尝试解析日期
                        datetime(dateValues, 'InputFormat', 'preserve');
                    catch
                        errors{end+1} = sprintf('日期列"%s"格式无效', originalColumnName);
                        isValid = false;
                    end
                end
                break;
            end
        end
    end
    
    % 检查必需字段组
    if isfield(validationRules, 'requiredFields') && ~isempty(validationRules.requiredFields)
        hasValidAlternative = false;
        
        for altGroup = validationRules.requiredFields
            groupFields = altGroup{1}; % 英文键名数组
            
            % 确保groupFields是cell数组
            if ischar(groupFields) || isstring(groupFields)
                groupFields = {char(groupFields)};
            end
            
            groupDisplayNames = {};
            
            % 转换英文键名为displayName
            for i = 1:length(groupFields)
                fieldKey = groupFields{i};
                
                % fieldKey可能是字符串或cell，需要统一处理
                if iscell(fieldKey)
                    fieldKeyStr = fieldKey{1};
                else
                    fieldKeyStr = char(fieldKey);
                end
                
                attributeNames = fieldnames(attributeConfig.attributes);
                for j = 1:length(attributeNames)
                    if strcmp(attributeNames{j}, fieldKeyStr)
                        groupDisplayNames{end+1} = attributeConfig.attributes.(fieldKeyStr).displayName;
                        break;
                    end
                end
            end
            
            % 检查是否有匹配的displayName
            for displayName = groupDisplayNames
                if ismember(displayName{1}, selectedAttributes)
                    hasValidAlternative = true;
                    break;
                end
            end
            
            if hasValidAlternative
                break;
            end
        end
        
        if ~hasValidAlternative
            % 构造错误消息 - 使用displayName
            errorMsgParts = {'必须至少包含以下字段组中的一个：'};
            for altGroup = validationRules.requiredFields
                groupFields = altGroup{1};
                
                % 确保groupFields是cell数组
                if ischar(groupFields) || isstring(groupFields)
                    groupFields = {char(groupFields)};
                end
                
                groupDisplayNames = {};
                
                % 转换为displayName用于显示
                for i = 1:length(groupFields)
                    fieldKey = groupFields{i};
                    
                    % fieldKey可能是字符串或cell，需要统一处理
                    if iscell(fieldKey)
                        fieldKeyStr = fieldKey{1};
                    else
                        fieldKeyStr = char(fieldKey);
                    end
                    
                    % 查找匹配的属性
                    attributeNames = fieldnames(attributeConfig.attributes);
                    foundMatch = false;
                    for j = 1:length(attributeNames)
                        if strcmp(attributeNames{j}, fieldKeyStr)
                            displayName = attributeConfig.attributes.(fieldKeyStr).displayName;
                            groupDisplayNames{end+1} = displayName;
                            foundMatch = true;
                            break;
                        end
                    end
                    
                    % 如果没找到匹配的属性，使用字段键名本身
                    if ~foundMatch
                        groupDisplayNames{end+1} = fieldKeyStr;
                    end
                end
                
                % 只有当找到字段名时才添加到错误消息中
                if ~isempty(groupDisplayNames)
                    errorMsgParts{end+1} = ['  • [' strjoin(groupDisplayNames, ' 或 ') ']'];
                else
                    errorMsgParts{end+1} = '  • [未知字段组]';
                end
            end
            errorMsg = strjoin(errorMsgParts, newline);
            errors{end+1} = errorMsg;
            isValid = false;
        end
    end
    
catch ME
    errors{end+1} = sprintf('数据验证过程出错：%s', ME.message);
    isValid = false;
end
end

function showValidationErrors(errors, parentFigure)
%SHOWVALIDATIONERRORS 显示验证错误
if isempty(errors)
    return;
end

errorMsg = '数据验证发现以下错误：' + newline + newline;
for i = 1:length(errors)
    errorMsg = errorMsg + sprintf('%d. %s', i, errors{i}) + newline;
end

errorMsg = errorMsg + newline + '请修正CSV文件后重新导入。';

uialert(parentFigure, errorMsg, '数据验证失败', 'Icon', 'error');
end

function mapping = createColumnMappingDialog(columnNames, csvData, ~, attributeConfig, themeName)
%CREATECOLUMNMAPPINGDIALOG 创建列映射配置对话框
% 添加themeName参数以支持主题应用
mapping = [];

% 使用传入的属性配置
attributeNames = fieldnames(attributeConfig.attributes);
reqAttributes = cell(size(attributeNames));
for i = 1:length(attributeNames)
    reqAttributes{i} = attributeConfig.attributes.(attributeNames{i}).displayName;
end

% 获取UI设置
uiSettings = struct('csvPreviewRows', 5, 'dialogSize', struct('width', 800, 'height', 600), ...
                    'dialogMinSize', struct('width', 600, 'height', 400));
if isfield(attributeConfig, 'uiSettings')
    uiSettings = attributeConfig.uiSettings;
end

% 创建模态对话框
dlg = uifigure('Name', 'CSV列映射配置', ...
              'Position', [100 100 uiSettings.dialogSize.width uiSettings.dialogSize.height], ...
              'WindowStyle', 'modal', 'Resize', 'on');

% 应用主题到对话框
ThemeManager.applyThemeToDialog(dlg, themeName);

% 设置最小窗口尺寸，确保界面元素的可用性
dlg.WindowState = 'normal'; 
try
    % 在较新的MATLAB版本中设置最小尺寸
    dlg.MinSize = [uiSettings.dialogMinSize.width uiSettings.dialogMinSize.height]; 
catch
    % 对于不支持MinSize的较老版本，忽略此设置
end

% 主网格布局
mainGrid = uigridlayout(dlg, [5, 1]);
mainGrid.RowHeight = {'fit', 'fit', 'fit', '1x', 'fit'};
mainGrid.Padding = [10 10 10 10]; % 添加整体内边距

% 标题
dialogTitle = uilabel(mainGrid);
dialogTitle.Text = '请配置CSV列到需求属性的映射关系';
dialogTitle.FontSize = 14;
dialogTitle.FontWeight = 'bold';
dialogTitle.HorizontalAlignment = 'center';

% 自动映射面板
autoMapPanel = uipanel(mainGrid, 'Title', '快速操作');
autoMapGrid = uigridlayout(autoMapPanel, [1, 3]);
autoMapGrid.ColumnWidth = {'fit', '1x', 'fit'};
autoMapGrid.Padding = [10 5 10 5];

% 自动映射按钮
autoMapButton = uibutton(autoMapGrid, 'Text', '⚡ 智能自动映射', ...
                        'Tooltip', '根据列名自动匹配标准属性');
autoMapButton.ButtonPushedFcn = @(~,~) performAutoMapping();

% 说明文字
autoMapLabel = uilabel(autoMapGrid);
autoMapLabel.Text = '未匹配的列将在确认时提示是否创建为自定义属性';
autoMapLabel.FontColor = [0.5 0.5 0.5];

% 重置按钮
resetButton = uibutton(autoMapGrid, 'Text', '重置', ...
                      'Tooltip', '将所有映射重置为"不导入"');
resetButton.ButtonPushedFcn = @(~,~) resetAllMappings();

% 数据预览表格
numRowsToShow = min(uiSettings.csvPreviewRows, height(csvData));
previewPanel = uipanel(mainGrid, 'Title', sprintf('CSV数据预览（前%d行）', numRowsToShow));
previewGrid = uigridlayout(previewPanel, [1, 1]);
previewGrid.Padding = [5 5 5 5]; % 设置内边距
previewTable = uitable(previewGrid);
previewTable.RowStriping = 'on'; % 启用行条纹以提高可读性

% 显示前n行数据作为预览（n由配置决定）
previewTable.Data = csvData(1:numRowsToShow, :);
previewTable.ColumnName = columnNames;

% 映射配置网格
mappingPanel = uipanel(mainGrid, 'Title', '列映射配置');
mappingGrid = uigridlayout(mappingPanel, [length(columnNames)+1, 3]);
mappingGrid.ColumnWidth = {'fit', '1x', 'fit'};
mappingGrid.Padding = [10 10 10 10]; % 设置内边距
mappingGrid.RowSpacing = 5; % 设置行间距
mappingGrid.ColumnSpacing = 10; % 设置列间距

% 表头
uilabel(mappingGrid, 'Text', 'CSV列名', 'FontWeight', 'bold');
uilabel(mappingGrid, 'Text', '映射到需求属性', 'FontWeight', 'bold');
uilabel(mappingGrid, 'Text', '是否必需', 'FontWeight', 'bold');

% 存储控件引用
mappingControls = struct();

% 追踪已使用的属性，避免重复映射
usedAttributesInit = {};

% 为每个CSV列创建映射配置
for i = 1:length(columnNames)
    % CSV列名标签
    uilabel(mappingGrid, 'Text', columnNames{i});
    
    % 属性映射下拉框
    dropdown = uidropdown(mappingGrid);
    dropdown.Items = reqAttributes;
    dropdown.Value = reqAttributes{1}; % 默认选择"不导入"
    
    % 根据列名智能匹配默认值
    columnName = lower(columnNames{i});
    bestMatch = '不导入';
    maxScore = 0;
    
    for attrIdx = 1:length(attributeNames)
        attrName = attributeNames{attrIdx};
        attrConfig = attributeConfig.attributes.(attrName);
        
        % 跳过"不导入"选项
        if strcmp(attrConfig.displayName, '不导入')
            continue;
        end
        
        % 跳过已经被映射的属性
        if ismember(attrConfig.displayName, usedAttributesInit)
            continue;
        end
        
        % 检查是否有匹配关键词
        if isfield(attrConfig, 'matchKeywords') && ~isempty(attrConfig.matchKeywords)
            keywords = attrConfig.matchKeywords;
            
            % 计算匹配分数
            score = 0;
            for keyIdx = 1:length(keywords)
                if contains(columnName, lower(keywords{keyIdx}))
                    score = score + 1;
                end
            end
            
            % 如果找到更好的匹配，更新最佳匹配
            if score > maxScore
                maxScore = score;
                bestMatch = attrConfig.displayName;
            end
        end
    end
    
    if maxScore > 0
        dropdown.Value = bestMatch;
        usedAttributesInit{end+1} = bestMatch;  % 记录已使用的属性
    end
    
    % 必需标志 - 根据配置确定
    isRequired = uicheckbox(mappingGrid);
    isRequired.Text = '';
    
    % 查找对应的属性配置以确定是否必需
    isRequiredByDefault = false;
    for attrIdx2 = 1:length(attributeNames)
        attrName = attributeNames{attrIdx2};
        attrConfig = attributeConfig.attributes.(attrName);
        if strcmp(attrConfig.displayName, dropdown.Value)
            isRequiredByDefault = isfield(attrConfig, 'required') && attrConfig.required;
            break;
        end
    end
    
    isRequired.Value = isRequiredByDefault;
    
    mappingControls(i).column = columnNames{i};
    mappingControls(i).dropdown = dropdown;
    mappingControls(i).required = isRequired;
end

% 按钮面板
buttonPanel = uipanel(mainGrid);
buttonGrid = uigridlayout(buttonPanel, [1, 3]);
buttonGrid.ColumnWidth = {'1x', 'fit', 'fit'};
buttonGrid.Padding = [10 10 10 10]; % 设置内边距
buttonGrid.ColumnSpacing = 10; % 设置按钮间距

uilabel(buttonGrid); % 占位符

% 确定按钮
okButton = uibutton(buttonGrid);
okButton.Text = '确定';
okButton.ButtonPushedFcn = @(~,~) validateAndClose();

% 取消按钮
cancelButton = uibutton(buttonGrid);
cancelButton.Text = '取消';
cancelButton.ButtonPushedFcn = @(~,~) close(dlg);

% 等待对话框关闭
uiwait(dlg);

    function performAutoMapping()
        % 执行智能自动映射
        matchCount = 0;
        usedAttributes = {};  % 追踪已使用的属性，避免重复映射
        
        for i = 1:length(columnNames)
            csvColName = columnNames{i};
            csvColNameLower = lower(csvColName);
            
            % 尝试匹配标准属性
            bestMatch = '不导入';
            maxScore = 0;
            
            for attrIdx = 1:length(attributeNames)
                attrName = attributeNames{attrIdx};
                attrConfig = attributeConfig.attributes.(attrName);
                
                % 跳过"不导入"选项
                if strcmp(attrConfig.displayName, '不导入')
                    continue;
                end
                
                % 跳过已经被映射的属性
                if ismember(attrConfig.displayName, usedAttributes)
                    continue;
                end
                
                % 检查关键词匹配
                if isfield(attrConfig, 'matchKeywords') && ~isempty(attrConfig.matchKeywords)
                    keywords = attrConfig.matchKeywords;
                    score = 0;
                    
                    for keyIdx = 1:length(keywords)
                        keyword = lower(keywords{keyIdx});
                        if contains(csvColNameLower, keyword) || contains(keyword, csvColNameLower)
                            % 完全匹配得分更高
                            if strcmp(csvColNameLower, keyword)
                                score = score + 10;
                            else
                                score = score + 1;
                            end
                        end
                    end
                    
                    if score > maxScore
                        maxScore = score;
                        bestMatch = attrConfig.displayName;
                    end
                end
            end
            
            % 如果找到匹配，更新下拉框并记录已使用的属性
            if maxScore > 0
                mappingControls(i).dropdown.Value = bestMatch;
                usedAttributes{end+1} = bestMatch;  % 记录已使用的属性
                matchCount = matchCount + 1;
            end
        end
        
        % 显示匹配结果
        uialert(dlg, sprintf('自动映射完成！\n已匹配 %d 列，未匹配 %d 列将在确认时处理。', ...
                           matchCount, length(columnNames) - matchCount), ...
               '自动映射结果', 'Icon', 'success');
    end

    function resetAllMappings()
        % 重置所有映射为"不导入"
        for i = 1:length(mappingControls)
            mappingControls(i).dropdown.Value = '不导入';
        end
    end

    function validateAndClose()
        % 验证映射配置
        tempMapping = struct();
        hasDuplicateMapping = false;
        usedAttributes = {};
        selectedAttributes = {};
        
        for ctrlIdx = 1:length(mappingControls)
            attrValue = mappingControls(ctrlIdx).dropdown.Value;
            if ~strcmp(attrValue, '不导入')
                % 检查重复映射
                if ismember(attrValue, usedAttributes)
                    hasDuplicateMapping = true;
                    break;
                end
                usedAttributes{end+1} = attrValue;
                selectedAttributes{end+1} = attrValue;
                
                % 创建有效的字段名（修复中文字段名问题）
                validFieldName = createValidFieldName(mappingControls(ctrlIdx).column);
                
                % 查找对应的属性配置
                targetProperty = '';
                for attrIdx3 = 1:length(attributeNames)
                    attrName = attributeNames{attrIdx3};
                    attrConfig = attributeConfig.attributes.(attrName);
                    if strcmp(attrConfig.displayName, attrValue)
                        targetProperty = attrConfig.targetProperty;
                        break;
                    end
                end
                
                tempMapping.(validFieldName) = struct(...
                    'attribute', attrValue, ...
                    'targetProperty', targetProperty, ...
                    'required', mappingControls(ctrlIdx).required.Value, ...
                    'originalColumn', mappingControls(ctrlIdx).column);  % 保存原始列名
            end
        end
        
        % 验证配置 - 使用JSON配置中的验证规则
        if hasDuplicateMapping
            uialert(dlg, '不能将多个CSV列映射到同一个需求属性！', '配置错误', 'Icon', 'error');
            return;
        end
        
        % 检查必需字段组
        validationRules = attributeConfig.validationRules;
        if isfield(validationRules, 'requiredFields') && ~isempty(validationRules.requiredFields)
            hasValidAlternative = false;
            
            for altGroup = validationRules.requiredFields
                groupFields = altGroup{1}; % 英文键名数组
                
                % 确保groupFields是cell数组
                if ischar(groupFields) || isstring(groupFields)
                    groupFields = {char(groupFields)};
                end
                
                groupMatched = false;
                
                % 转换英文键名为displayName进行比较
                for i = 1:length(groupFields)
                    fieldKey = groupFields{i};
                    
                    % fieldKey可能是字符串或cell，需要统一处理
                    if iscell(fieldKey)
                        fieldKeyStr = fieldKey{1};
                    else
                        fieldKeyStr = char(fieldKey);
                    end
                    
                    % 根据英文键名查找displayName
                    attributeNames = fieldnames(attributeConfig.attributes);
                    for j = 1:length(attributeNames)
                        if strcmp(attributeNames{j}, fieldKeyStr)
                            displayName = attributeConfig.attributes.(fieldKeyStr).displayName;
                            if ismember(displayName, selectedAttributes)
                                groupMatched = true;
                                break;
                            end
                        end
                    end
                    if groupMatched
                        break;
                    end
                end
                
                if groupMatched
                    hasValidAlternative = true;
                    break;
                end
            end
            
            if ~hasValidAlternative
                % 构造错误消息 - 使用displayName
                errorMsgParts = {'必须至少映射以下字段组中的一个：'};
                for altGroup = validationRules.requiredFields
                    groupFields = altGroup{1};
                    
                    % 确保groupFields是cell数组
                    if ischar(groupFields) || isstring(groupFields)
                        groupFields = {char(groupFields)};
                    end
                    
                    groupDisplayNames = {};
                    
                    % 转换为displayName用于显示
                    for i = 1:length(groupFields)
                        fieldKey = groupFields{i};
                        
                        % fieldKey可能是字符串或cell，需要统一处理
                        if iscell(fieldKey)
                            fieldKeyStr = fieldKey{1};
                        else
                            fieldKeyStr = char(fieldKey);
                        end
                        
                        % 查找匹配的属性
                        attributeNames = fieldnames(attributeConfig.attributes);
                        foundMatch = false;
                        for j = 1:length(attributeNames)
                            if strcmp(attributeNames{j}, fieldKeyStr)
                                displayName = attributeConfig.attributes.(fieldKeyStr).displayName;
                                groupDisplayNames{end+1} = displayName;
                                foundMatch = true;
                                break;
                            end
                        end
                        
                        % 如果没找到匹配的属性，使用字段键名本身
                        if ~foundMatch
                            fprintf('警告: 未找到字段 "%s" 的配置\n', fieldKeyStr);
                            groupDisplayNames{end+1} = fieldKeyStr;
                        end
                    end
                    
                    % 只有当找到字段名时才添加到错误消息中
                    if ~isempty(groupDisplayNames)
                        errorMsgParts{end+1} = ['  • [' strjoin(groupDisplayNames, ' 或 ') ']'];
                    else
                        errorMsgParts{end+1} = '  • [未知字段组]';
                    end
                end
                
                errorMsg = strjoin(errorMsgParts, newline);
                uialert(dlg, errorMsg, '配置错误', 'Icon', 'error');
                return;
            end
        end
        
        mapping = tempMapping;
        close(dlg);
    end
end

function registerCustomAttributes(targetReqSet, columnMapping, attributeConfig)
%REGISTERCUSTOMATTRIBUTES 在需求集上注册自定义属性
%   根据列映射和属性配置，在需求集级别注册所有需要的自定义属性
%   基于配置文件中的comboboxOptions动态注册下拉框选项

try
    mappedColumns = fieldnames(columnMapping);
    
    % 获取需求集已有的自定义属性
    existingAttrs = {};
    try
        existingAttrs = targetReqSet.CustomAttributeNames;
    catch
        % 如果没有自定义属性，CustomAttributeNames可能不存在
    end
    
    % 注册所有已映射的属性
    for i = 1:length(mappedColumns)
        mappingInfo = columnMapping.(mappedColumns{i});
        attribute = mappingInfo.attribute;
        targetProperty = mappingInfo.targetProperty;
        
        % 跳过不导入和标准属性
        if strcmp(attribute, '不导入') || isempty(targetProperty)
            continue;
        end
        
        % 检查是否使用CSV原始列名作为自定义属性名
        useOriginalName = false;
        if isfield(mappingInfo, 'useOriginalColumnAsAttributeName')
            useOriginalName = mappingInfo.useOriginalColumnAsAttributeName;
        end
        
        % 只处理需要注册的自定义属性
        [needsRegistration, attrType, attrOptions] = determineAttributeRegistration(targetProperty, attribute, useOriginalName, attributeConfig);
        
        % 注册自定义属性（如果尚未存在）
        if needsRegistration
            % 根据原则4：使用原始CSV列名或中文显示名作为属性名
            if useOriginalName
                attrName = mappingInfo.originalColumn;  % 使用CSV原始列名
                fprintf('准备注册动态自定义属性: %s (来自CSV列名)\n', attrName);
            else
                attrName = attribute;  % 使用中文显示名
            end
            
            % 检查属性是否已存在
            if ~ismember(attrName, existingAttrs)
                try
                    % 根据MATLAB官方文档，Combobox类型使用'List'参数指定选项
                    if strcmp(attrType, 'Combobox') && ~isempty(attrOptions)
                        addAttribute(targetReqSet, attrName, attrType, ...
                                   'List', attrOptions, ...
                                   'Description', sprintf('从CSV导入的%s属性', attribute));
                        fprintf('已注册自定义属性: %s (%s, %d个选项)\n', attrName, attrType, length(attrOptions));
                    else
                        addAttribute(targetReqSet, attrName, attrType, ...
                                   'Description', sprintf('从CSV导入的%s属性', attribute));
                        fprintf('已注册自定义属性: %s (%s)\n', attrName, attrType);
                    end
                catch ME
                    fprintf('错误：注册自定义属性"%s"失败：%s\n', attrName, ME.message);
                    fprintf('  - 属性名称: %s\n', attrName);
                    fprintf('  - 属性类型: %s\n', attrType);
                    if strcmp(attrType, 'Combobox')
                        fprintf('  - 选项列表: %s\n', strjoin(attrOptions, ', '));
                    end
                end
            else
                fprintf('自定义属性已存在: %s\n', attrName);
            end
        end
    end
    
    % 注册配置文件中定义了默认值的属性（即使CSV中未提供）
    % 基于用户需求：作为导入时的默认值
    registerDefaultValueAttributes(targetReqSet, attributeConfig, existingAttrs);
    
catch ME
    fprintf('警告：注册自定义属性过程出错：%s\n', ME.message);
end
end

function [needsRegistration, attrType, attrOptions] = determineAttributeRegistration(targetProperty, attribute, useOriginalName, attributeConfig)
%DETERMINEATTRIBUTEREGISTRATION 确定属性是否需要注册及其类型
%   基于配置文件中的定义，确定属性的注册方式
%   type字段必须为：Edit、Checkbox、Combobox、DateTime 之一

needsRegistration = false;
attrType = 'Edit';  % 默认类型
attrOptions = {};

if useOriginalName
    % 使用CSV原始列名，直接注册为Edit类型的自定义属性
    needsRegistration = true;
    attrType = 'Edit';
    attrOptions = {};
    return;
end

% 标准属性不需要注册
standardProperties = {'Id', 'Summary', 'Description', 'CreatedBy', 'CreatedOn', 'ModifiedOn'};
if ismember(targetProperty, standardProperties)
    return;
end

% 查找配置文件中的属性定义
attributeNames = fieldnames(attributeConfig.attributes);
for i = 1:length(attributeNames)
    attrName = attributeNames{i};
    attrConfig = attributeConfig.attributes.(attrName);
    
    % 通过displayName或targetProperty匹配
    if strcmp(attrConfig.displayName, attribute) || strcmp(attrConfig.targetProperty, targetProperty)
        needsRegistration = true;
        
        % 获取并验证type字段
        if isfield(attrConfig, 'type')
            attrType = attrConfig.type;
            
            % 验证type字段必须为有效的SLREQX类型
            validTypes = {'Edit', 'Checkbox', 'Combobox', 'DateTime', 'skip'};
            if ~ismember(attrType, validTypes)
                error('配置错误：属性"%s"的type字段"%s"无效，必须为以下之一：%s', ...
                    attrName, attrType, strjoin(validTypes, ', '));
            end
            
            % Combobox类型需要获取选项
            if strcmp(attrType, 'Combobox')
                if isfield(attrConfig, 'comboboxOptions')
                    attrOptions = attrConfig.comboboxOptions;
                else
                    error('配置错误：属性"%s"的type为Combobox，但未定义comboboxOptions', attrName);
                end
            end
        else
            attrType = 'Edit';  % 默认类型
        end
        return;
    end
end

% 如果在配置中未找到，但targetProperty不为空，注册为Edit类型
if ~isempty(targetProperty)
    needsRegistration = true;
    attrType = 'Edit';
end
end

function registerDefaultValueAttributes(targetReqSet, attributeConfig, existingAttrs)
%REGISTERDEFAULTVALUEATTRIBUTES 注册配置文件中定义了默认值的属性
%   这些属性即使CSV中未提供，也需要注册到需求集中，以便应用默认值
%   基于用户需求：作为导入时的默认值

attributeNames = fieldnames(attributeConfig.attributes);

for i = 1:length(attributeNames)
    attrName = attributeNames{i};
    attrConfig = attributeConfig.attributes.(attrName);
    
    % 跳过"不导入"选项
    if strcmp(attrConfig.displayName, '不导入')
        continue;
    end
    
    % 只处理有默认值的属性
    if ~isfield(attrConfig, 'defaultValue') || isempty(attrConfig.defaultValue)
        continue;
    end
    
    % 跳过标准属性
    targetProperty = attrConfig.targetProperty;
    standardProperties = {'Id', 'Summary', 'Description', 'CreatedBy', 'CreatedOn', 'ModifiedOn'};
    if ismember(targetProperty, standardProperties)
        continue;
    end
    
    % 使用displayName作为属性名
    displayName = attrConfig.displayName;
    
    % 检查属性是否已存在
    if ismember(displayName, existingAttrs)
        fprintf('默认值属性已存在: %s\n', displayName);
        continue;
    end
    
    % 确定属性类型和选项（直接使用SLREQX类型名）
    attrType = 'Edit';
    attrOptions = {};
    if isfield(attrConfig, 'type')
        attrType = attrConfig.type;
        
        % 验证type字段
        validTypes = {'Edit', 'Checkbox', 'Combobox', 'DateTime', 'skip'};
        if ~ismember(attrType, validTypes)
            fprintf('警告：属性"%s"的type字段"%s"无效，使用默认值Edit\n', displayName, attrType);
            attrType = 'Edit';
        end
        
        % Combobox类型需要获取选项
        if strcmp(attrType, 'Combobox')
            if isfield(attrConfig, 'comboboxOptions')
                attrOptions = attrConfig.comboboxOptions;
            else
                fprintf('警告：属性"%s"的type为Combobox，但未定义comboboxOptions\n', displayName);
            end
        end
    end
    
    % 注册属性
    try
        if strcmp(attrType, 'Combobox') && ~isempty(attrOptions)
            addAttribute(targetReqSet, displayName, attrType, ...
                       'List', attrOptions, ...
                       'Description', sprintf('%s（默认值：%s）', attrConfig.description, attrConfig.defaultValue));
            fprintf('已注册默认值属性: %s (%s, 默认值=%s, %d个选项)\n', displayName, attrType, attrConfig.defaultValue, length(attrOptions));
        else
            addAttribute(targetReqSet, displayName, attrType, ...
                       'Description', sprintf('%s（默认值：%s）', attrConfig.description, attrConfig.defaultValue));
            fprintf('已注册默认值属性: %s (%s, 默认值=%s)\n', displayName, attrType, attrConfig.defaultValue);
        end
    catch ME
        fprintf('警告：注册默认值属性"%s"失败：%s\n', displayName, ME.message);
    end
end
end

function validName = createValidFieldName(originalName)
%CREATEVALIDFIELDNAME 将任意字符串转换为有效的MATLAB字段名
% 移除或替换无效字符，确保字段名以字母开头

validName = originalName;

% 替换中文和特殊字符为下划线
validName = regexprep(validName, '[^\w]', '_');

% 如果以数字开头，添加前缀
if ~isempty(validName) && isstrprop(validName(1), 'digit')
    validName = ['col_' validName];
end

% 如果为空或只包含无效字符，生成默认名称
if isempty(validName) || ~isvarname(validName)
    validName = sprintf('column_%d', randi(10000));
end

% 确保是有效的变量名
validName = matlab.lang.makeValidName(validName);
end

function success = executeRequirementImport(csvData, columnMapping, targetReqSet, isNewFile, parentFigure, messageTextArea, attributeConfig)
%EXECUTEREQUIREMENTIMPORT 执行需求导入
%   包含应用默认值逻辑（基于用户需求：作为导入时的默认值）

try
    mappedColumns = fieldnames(columnMapping);
    numRows = height(csvData);
    
    % 创建进度对话框
    progressDlg = uiprogressdlg(parentFigure, 'Title', '导入需求', ...
        'Message', '正在导入需求数据...', 'Indeterminate', 'off');
    
    importedCount = 0;
    skippedCount = 0;
    
    for rowIdx = 1:numRows
        try
            % 更新进度
            progressDlg.Value = rowIdx / numRows;
            progressDlg.Message = sprintf('正在导入第%d/%d个需求...', rowIdx, numRows);
            
            % 创建需求对象
            if ~isNewFile
                % 检查需求ID是否已存在（如果有ID映射）
                req = [];
                for j = 1:length(mappedColumns)
                    mappingInfo = columnMapping.(mappedColumns{j});
                    % 检查是否映射到ID属性
                    if strcmp(mappingInfo.targetProperty, 'Id')
                        reqId = string(csvData.(mappingInfo.originalColumn)(rowIdx));
                        existingReqs = slreq.find('Type', 'Requirement', 'Id', char(reqId));
                        if ~isempty(existingReqs)
                            % 更新现有需求
                            req = existingReqs(1);
                            break;
                        end
                    end
                end
                
                if isempty(req)
                    req = targetReqSet.add;
                end
            else
                req = targetReqSet.add;
            end
            
            % 设置需求属性（来自CSV）
            for j = 1:length(mappedColumns)
                mappingInfo = columnMapping.(mappedColumns{j});
                originalColumnName = mappingInfo.originalColumn;
                attribute = mappingInfo.attribute;
                targetProperty = mappingInfo.targetProperty;
                cellValue = csvData.(originalColumnName)(rowIdx);
                
                % 处理不同类型的数据
                if ismissing(cellValue) || isempty(cellValue)
                    continue;
                end
                
                valueStr = char(string(cellValue));
                
                % 根据目标属性设置需求属性
                try
                    if isempty(targetProperty)
                        continue; % 跳过没有目标属性的字段
                    end
                    
                    % 标准属性直接赋值
                    switch targetProperty
                        case 'Id'
                            req.Id = valueStr;
                        case 'Summary'
                            req.Summary = valueStr;
                        case 'Description'
                            req.Description = valueStr;
                        case {'CreatedBy', 'CreatedOn', 'ModifiedOn'}
                            % 这些是只读属性，无法设置，跳过
                            % 基于第2条规则：不fallback到Description，明确跳过
                            continue;
                        case {'Status', 'Revision', 'Priority'}
                            % 这些必须作为自定义属性设置（已在ReqSet注册）
                            % 使用中文显示名作为属性名
                            setAttribute(req, attribute, valueStr);
                        otherwise
                            % 其他自定义属性（已在ReqSet注册）
                            if startsWith(targetProperty, 'CustomAttribute_') || ~isempty(targetProperty)
                                % 检查是否使用CSV原始列名
                                if isfield(mappingInfo, 'useOriginalColumnAsAttributeName') && ...
                                   mappingInfo.useOriginalColumnAsAttributeName
                                    % 使用CSV原始列名作为属性名
                                    setAttribute(req, originalColumnName, valueStr);
                                else
                                    % 使用中文显示名作为属性名
                                    setAttribute(req, attribute, valueStr);
                                end
                            end
                    end
                catch ME
                    % 基于第2条规则：明确报告错误，不silent处理
                    fprintf('警告：第%d行属性"%s"设置失败：%s\n', rowIdx, attribute, ME.message);
                end
            end
            
            % 应用默认值（如果CSV中未提供该属性）
            % 基于用户需求：作为导入时的默认值
            applyDefaultValues(req, columnMapping, attributeConfig);
            
            importedCount = importedCount + 1;
            
        catch ME
            fprintf('警告：第%d行导入失败：%s\n', rowIdx, ME.message);
            skippedCount = skippedCount + 1;
            continue;
        end
        
        % 定期更新UI
        if mod(rowIdx, 10) == 0
            drawnow;
        end
    end
    
    % 保存需求集
    progressDlg.Message = '正在保存需求集文件...';
    progressDlg.Indeterminate = 'on';
    drawnow;
    
    targetReqSet.save();
    
    % 关闭进度对话框
    close(progressDlg);
    
    % 显示导入结果
    resultMsg = sprintf('导入完成！\n成功导入：%d个需求\n跳过/失败：%d个需求', ...
        importedCount, skippedCount);
    
    uialert(parentFigure, resultMsg, '导入完成', 'Icon', 'success');
    
    updateMessage(messageTextArea, sprintf('CSV导入完成：成功%d个，跳过%d个', importedCount, skippedCount));
    
    success = true;
    
catch ME
    if exist('progressDlg', 'var') && isvalid(progressDlg)
        close(progressDlg);
    end
    uialert(parentFigure, sprintf('导入过程发生错误：\n%s', ME.message), '导入失败', 'Icon', 'error');
    success = false;
end
end

function applyDefaultValues(req, columnMapping, attributeConfig)
%APPLYDEFAULTVALUES 应用配置文件中定义的默认值
%   对于CSV中未提供的属性，如果配置中定义了默认值，则应用该默认值
%   基于用户需求：作为导入时的默认值

% 获取所有已映射的属性（CSV中提供的）
mappedAttributes = {};
mappedColumns = fieldnames(columnMapping);
for i = 1:length(mappedColumns)
    mappingInfo = columnMapping.(mappedColumns{i});
    if ~strcmp(mappingInfo.attribute, '不导入')
        mappedAttributes{end+1} = mappingInfo.attribute;
    end
end

% 遍历配置文件中的所有属性
attributeNames = fieldnames(attributeConfig.attributes);
for i = 1:length(attributeNames)
    attrName = attributeNames{i};
    attrConfig = attributeConfig.attributes.(attrName);
    
    % 跳过"不导入"选项
    if strcmp(attrConfig.displayName, '不导入')
        continue;
    end
    
    % 只处理有默认值的属性
    if ~isfield(attrConfig, 'defaultValue') || isempty(attrConfig.defaultValue)
        continue;
    end
    
    % 跳过标准属性（Id、Summary、Description等）
    targetProperty = attrConfig.targetProperty;
    standardProperties = {'Id', 'Summary', 'Description', 'CreatedBy', 'CreatedOn', 'ModifiedOn'};
    if ismember(targetProperty, standardProperties)
        continue;
    end
    
    % 如果CSV中已提供该属性，跳过（不覆盖CSV中的值）
    if ismember(attrConfig.displayName, mappedAttributes)
        continue;
    end
    
    % 应用默认值
    try
        setAttribute(req, attrConfig.displayName, attrConfig.defaultValue);
        % fprintf('已应用默认值: %s = %s\n', attrConfig.displayName, attrConfig.defaultValue);
    catch ME
        fprintf('警告：应用默认值失败 %s = %s: %s\n', attrConfig.displayName, attrConfig.defaultValue, ME.message);
    end
end
end

function updatedMapping = handleUnmatchedColumns(columnMapping, columnNames, csvData, parentFigure, mappingSettings, themeName)
%HANDLEUNMATCHEDCOLUMNS 处理未匹配的CSV列，询问用户是否创建为自定义属性
%   显示对话框让用户选择未匹配的列是否要使用原始列名创建自定义属性
% 添加themeName参数以支持主题应用

updatedMapping = columnMapping;

% 找出未映射的列
mappedColumns = fieldnames(columnMapping);
unmappedColumns = {};
unmappedColumnData = {};

for i = 1:length(columnNames)
    csvColName = columnNames{i};
    isMapped = false;
    
    % 检查该列是否已映射
    for j = 1:length(mappedColumns)
        mappingInfo = columnMapping.(mappedColumns{j});
        if strcmp(mappingInfo.originalColumn, csvColName) && ~strcmp(mappingInfo.attribute, '不导入')
            isMapped = true;
            break;
        end
    end
    
    if ~isMapped
        unmappedColumns{end+1} = csvColName;
        % 获取该列的示例数据（前3行）
        sampleData = csvData.(csvColName);
        numSamples = min(3, height(csvData));
        sampleStr = '';
        for k = 1:numSamples
            val = sampleData(k);
            if iscell(val)
                val = val{1};
            end
            sampleStr = sprintf('%s%s', sampleStr, string(val));
            if k < numSamples
                sampleStr = sprintf('%s, ', sampleStr);
            end
        end
        unmappedColumnData{end+1} = sampleStr;
    end
end

% 如果没有未映射的列，直接返回
if isempty(unmappedColumns)
    return;
end

% 创建未匹配列处理对话框
confirmDlg = uifigure('Name', '未匹配列处理', ...
                     'Position', [200 200 700 500], ...
                     'WindowStyle', 'modal');

% 应用主题到对话框
ThemeManager.applyThemeToDialog(confirmDlg, themeName);

mainGrid = uigridlayout(confirmDlg, [4, 1]);
mainGrid.RowHeight = {'fit', 'fit', '1x', 'fit'};
mainGrid.Padding = [15 15 15 15];

% 标题
titleLabel = uilabel(mainGrid);
titleLabel.Text = sprintf('发现 %d 个未匹配的CSV列', length(unmappedColumns));
titleLabel.FontSize = 14;
titleLabel.FontWeight = 'bold';

% 说明文字
descLabel = uilabel(mainGrid);
descLabel.Text = '以下列未匹配到标准需求属性，请选择要创建为自定义属性的列（将使用CSV原始列名）：';
descLabel.WordWrap = 'on';

% 列选择列表面板
listPanel = uipanel(mainGrid, 'Title', '未匹配的列');
listGrid = uigridlayout(listPanel, [length(unmappedColumns)+1, 3]);
listGrid.ColumnWidth = {'fit', '1x', '2x'};
listGrid.RowHeight = repmat({'fit'}, 1, length(unmappedColumns)+1);
listGrid.Padding = [10 10 10 10];
listGrid.RowSpacing = 8;

% 表头
uilabel(listGrid, 'Text', '选择', 'FontWeight', 'bold');
uilabel(listGrid, 'Text', 'CSV列名', 'FontWeight', 'bold');
uilabel(listGrid, 'Text', '示例数据', 'FontWeight', 'bold');

% 创建复选框和标签
checkboxes = {};
for i = 1:length(unmappedColumns)
    % 复选框
    cb = uicheckbox(listGrid, 'Text', '');
    cb.Value = mappingSettings.unmatchedColumnsAsCustom;  % 根据配置设置默认值
    checkboxes{end+1} = cb;
    
    % 列名标签
    colNameLabel = uilabel(listGrid);
    colNameLabel.Text = unmappedColumns{i};
    colNameLabel.FontWeight = 'bold';
    
    % 示例数据标签
    sampleLabel = uilabel(listGrid);
    sampleLabel.Text = unmappedColumnData{i};
    sampleLabel.FontColor = [0.5 0.5 0.5];
end

% 按钮面板
buttonPanel = uipanel(mainGrid);
buttonGrid = uigridlayout(buttonPanel, [1, 4]);
buttonGrid.ColumnWidth = {'1x', 'fit', 'fit', 'fit'};
buttonGrid.Padding = [10 10 10 10];
buttonGrid.ColumnSpacing = 10;

uilabel(buttonGrid); % 占位符

% 全选按钮
selectAllButton = uibutton(buttonGrid, 'Text', '全选');
selectAllButton.ButtonPushedFcn = @(~,~) selectAllCheckboxes(true);

% 全不选按钮
deselectAllButton = uibutton(buttonGrid, 'Text', '全不选');
deselectAllButton.ButtonPushedFcn = @(~,~) selectAllCheckboxes(false);

% 确定按钮
okButton = uibutton(buttonGrid, 'Text', '确定');
okButton.ButtonPushedFcn = @(~,~) applySelection();
okButton.BackgroundColor = [0.2 0.6 0.2];
okButton.FontColor = 'white';

uiwait(confirmDlg);

    function selectAllCheckboxes(value)
        for idx = 1:length(checkboxes)
            checkboxes{idx}.Value = value;
        end
    end

    function applySelection()
        % 处理用户选择
        for idx = 1:length(checkboxes)
            if checkboxes{idx}.Value
                % 用户选择创建自定义属性
                csvColName = unmappedColumns{idx};
                
                % 创建有效的字段名
                validFieldName = createValidFieldName(csvColName);
                
                % 添加到映射中，标记使用原始列名
                updatedMapping.(validFieldName) = struct(...
                    'attribute', csvColName, ...  % 使用CSV原始列名作为属性名
                    'targetProperty', sprintf('CustomAttribute_Dynamic_%s', validFieldName), ...
                    'required', false, ...
                    'originalColumn', csvColName, ...
                    'useOriginalColumnAsAttributeName', true);  % 关键标记
                
                fprintf('已添加自定义属性映射: %s -> %s\n', csvColName, csvColName);
            end
        end
        
        close(confirmDlg);
    end
end
